#ifndef __INC_QGRMODEL_H__
#define __INC_QGRMODEL_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qgrdpct.h"

//////////////////////////////////////////////////////////////////////////
// SMM

//
enum qgSmmFrameType
{
	QGSMMFRM_DUMMY,
	QGSMMFRM_BONE,
	QGSMMFRM_MESH,
};

//
enum qgSmmAnimType
{
	QGSMMANM_UNIT,
	QGSMMANM_DATA,
};

//
struct qgSmmDrawData : public tpl::MemZero
{
	kint				faces;
	kint				indexes;
	kint				vertices;

	tpl::VecArray<kint>				_i;
	tpl::VecArray<tpl::Vec3>		_v;
	tpl::VecArray<tpl::Vec3>		_n;
	tpl::VecArray<tpl::Vec2>		_t;
	tpl::VecArray<kuint>			_d;
	tpl::VecArray<tpl::Vec3>		_b;
	tpl::VecArray<tpl::Vec3>		_a;
	tpl::VecArray<qgVertexWeight>	_w;
};

// frame
class KAPI qgSmmFrame : public qgDpct
{
	KCH_SUBCLASS(qgSmmFrame, qgDpct);
	KCH_FRIEND(qgSmm);

protected:
	qgSmmFrameType		_frmtype;
	kint				_insts;

	kint				_pid;
	kint				_eid;

	float				_len;
	tpl::Mat4			_defm;
	tpl::TrFm			_orgm;

protected:
	qgSmmFrame(qgSmmFrameType frmtype = QGSMMFRM_DUMMY);
	~qgSmmFrame() {};

public:
	qgSmmFrameType GetFrameType() { return _frmtype; }
	kint GetInsts() { return _insts; }
	kint GetPid() { return _pid; }
	const tpl::Mat4& GetDefTM() { return _defm; }
	const tpl::TrFm& GetOrgTM() { return _orgm; }
	float GetLength() { return _len; }
	bool IsAnim() { return !K_OFLAG(_insts, 0x1000); }

	void SetPid(kint pid) { _pid = pid; }
	void SetEId(kint exid) { _eid = exid; }
	void TurnAnim(bool value) { K_UFLAG(&_insts, 0x1000, !value); }
};

// bone
class KAPI qgSmmBone : public qgSmmFrame
{
	KCH_SUBCLASS(qgSmmBone, qgSmmFrame);
	KCH_FRIEND(qgSmm);
	KCH_FRIEND(qgSmmSkin);

protected:
	tpl::Mat4			_subm;
	tpl::Mat4			_cmbn;

protected:
	qgSmmBone(qgSmmFrameType frmtype = QGSMMFRM_BONE);
	~qgSmmBone() {};

public:
	K_OVR bool Update(float adv);

	kint GetBoneId() { return _eid; }
	const tpl::Mat4& GetCombineTM() { return _cmbn; }
	const tpl::Mat4& GetSubTM() { return _subm; }

	void SetBoneId(kint bid) { _eid = bid; }
	void SetCombineTM(const tpl::Mat4& m) { _cmbn = m; }
	void SetSubTM(const tpl::Mat4& m) { _subm = m; }
};

// mesh
class KAPI qgSmmMesh : public qgSmmFrame
{
	KCH_SUBCLASS(qgSmmMesh, qgSmmFrame);
	KCH_FRIEND(qgSmm);

protected:
	qgSmmDrawData*		_data;

	qgLayout*			_lo;
	qgBuffer*			_ib;
	qgBuffer*			_vdraw;
	qgBuffer*			_vnorm;
	qgBuffer*			_vweight;

	kint				_faces;
	kint				_indexes;

	qgTexture*			_tex[3];
	qgRasz*				_rasz;
	qgBlnd*				_blnd;
	qgShader*			_shd;

protected:
	qgSmmMesh(qgSmmFrameType frmtype = QGSMMFRM_MESH);
	~qgSmmMesh();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	void DrawOnly();

	kint GetResId() { return _eid; }
	
	void SetResId(kint id) { _eid = id; }

	//
	qgTexture* GetTexture(kint nth) { return _tex[nth]; }
	void SetTexture(kint nth, qgTexture* tex) { kobj_unload(_tex[nth]); _tex[nth] = tex; kobj_load(tex); }
	void SetLayout(qgLayout* lo) { kobj_unload(_lo); _lo = lo; kobj_load(lo); }
	void SetShader(qgShader* shd) { kobj_unload(_shd); _shd = shd; kobj_load(_shd); }
	void SetRasz(qgRasz* rasz) { kobj_unload(_rasz); _rasz = rasz; kobj_load(_rasz); }

protected:
	virtual void _GenerateMesh(qgSmm* smm);
};

// skin
class KAPI qgSmmSkin : public qgSmmMesh
{
	KCH_SUBCLASS(qgSmmSkin, qgSmmMesh);
	KCH_FRIEND(qgSmm);

protected:
	kint				_cmbns;
	tpl::Mat4*			_pcmbn;
	tpl::Mat4**			_pinds;

protected:
	qgSmmSkin(qgSmmFrameType frmtype = QGSMMFRM_MESH);
	~qgSmmSkin();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	void DrawOnly();

protected:
	K_OVR void _GenerateMesh(qgSmm* smm);
};

// smm
class KAPI qgSmm : public qgSmmBone
{
	KCH_SUBCLASS(qgSmm, qgSmmBone);

public:
	typedef tpl::VecArray<kint>				int_array;
	typedef tpl::VecArray<qgTexture*>		texture_array;
	typedef tpl::VecArray<qgSmmFrame*>		frame_array;
	typedef tpl::VecArray<qgSmmFrame*>		dummy_array;
	typedef tpl::VecArray<qgSmmBone*>		bone_array;
	typedef tpl::VecArray<qgSmmMesh*>		mesh_array;

protected:
	qgSmmFrame*			_pivot;

	int_array			_mtfs;
	texture_array		_texs;
	frame_array			_frms;
	dummy_array			_dmms;
	bone_array			_bons;
	mesh_array			_mshs;

	kint				_dcnt;

protected:
	qgSmm();
	~qgSmm();

public:
	K_OVR bool Update(float adv);
	K_OVR void Draw();

	kint UpdateFrame(float adv);
	void DrawOnly();

	qgSmmFrame* GetFrame(const char* name);
	qgSmmFrame* FindFrame(kint id);

	kint GetMaterialFlagNth(kint nth);
	qgTexture* GetTextureNth(kint nth);
	qgSmmFrame* GetFrameNth(kint nth);
	qgSmmFrame* GetDummyNth(kint nth);
	qgSmmBone* GetBoneNth(kint nth);
	qgSmmMesh* GetMeshNth(kint nth);

	kint GetTextureCount() { return _texs.Count(); };
	kint GetFrameCount() { return _frms.Count(); };
	kint GetDummyCount() { return _dmms.Count(); };
	kint GetBoneCount() { return _bons.Count(); };
	kint GetMeshCount() { return _mshs.Count(); };

private:
	bool _PrivateRead520(const kbyte* data, kint size, kint flags, const char* filename);

public:
	static qgSmm* New(kint id, const char* filename, kint flags);
};

//
struct qgTimeVec3
{
	float				t;
	tpl::Vec3			v;
};

//
struct qgTimeQuat
{
	float				t;
	tpl::Quat			q;
};

//
struct qgSmaKey : tpl::MemZero
{
	int					ref;

	kuint				hash;
	tpl::BlueStr<64>	name;

	kint				id;
	kint				pid;
	kint				bid;
	float				len;

	tpl::VecArray<qgTimeVec3>	locs;
	tpl::VecArray<qgTimeQuat>	rots;
	tpl::VecArray<qgTimeVec3>	scls;
};

//
struct qgSmaUnit : tpl::MemZero
{
	int					index;

	kuint				hash;
	tpl::BlueStr<64>	name;

	tpl::Vec2			dur;
	float				last;
	float				speed;
	float				cur;

	kint				type;
	kint				cmpr;

	tpl::VecArray<qgSmaKey*>	keys;
	tpl::VecArray<qgSmmFrame*>	links;

	qgSmm*				smm;

	void _UpdatePosition(float adv);
};

//
class KAPI qgSma : public qgObj
{
	KCH_SUBCLASS(qgSma, qgObj);

protected:
	tpl::BlueStr<64>	_name;

	kushort				_version;
	kushort				_type;

	kint				_flags;

	bool				_isblend;
	bool				_isloop;
	bool				_isrev;

	float				_speed;

	tpl::VecArray<qgSmaUnit*>	_units;
	qgSmaUnit*			_cur;

	qgSmm*				_smm;

protected:
	qgSma();
	~qgSma();

public:
	void Link(qgSmm* smm);

	bool IsAnimate();
	bool IsFinish();

	bool SetUnit(const char* name);
	bool SetUnitNth(kint n);
	void SetSpeed(float f);
	void TurnLoop(bool value);

	bool Update(float adv);

	qgSmaUnit* GetUnit() { return _cur; }

private:
	bool _PrivateRead520(const kbyte* data, kint size, kint flags);

public:
	static qgSma* New(const char* filename, kint flags);
};

#endif	// __INC_QGRMODEL_H__
